﻿#include "ThreadCache.h"
#include "Common.h"
#include "CentralCache.h"

void* ThreadCache::FetchFromCentralCache(size_t index, size_t size)
{
	//这里使用了慢开始的反馈调节算法
	//这里要从1开始慢慢增长，并且将SizeClass::NumMoveSize(size)作为上限，再大就不会增了
	//该算法的优点:
	//1. 首先最开始不会向central cache一次批量要太多，因为太多可能用不完
	//2. 如果你不断有这个size大小内存的需求，那么batchNum就会不断增长，直到上限
	size_t batchNum = min(_freeLists[index].MaxSize() , SizeClass::NumMoveSize(size));
	
	//此时第一次申请就给一个对象，第二次申请就给两个对象，到最大给512个对象为止
	//这样可以让thread Cache中存在一些对象，下次就不需要向Central Cache申请
	if (_freeLists[index].MaxSize() == batchNum)
	{ //因为MaxSize函数用的是引用返回，所以该对象的返回值_maxsize本身也一直在改变
		_freeLists[index].MaxSize() += 1;
	}

	void* start = nullptr; 
	void* end = nullptr;
	//此处actualNum表示实际上收到了多少个，因为此时可能没有那么多空间
	size_t actualNum = CentralCache::GetInstance()->FetchRangeObj(start , end , batchNum , size);
	assert(actualNum > 0);

	if (actualNum == 1)
	{ //此时表示当前实际申请到的就只有一个，那么就直接将start返回就行了，因为start和end是一样的
		assert(start == end);
		return start;
	} 

	else
	{ //此时要将头返回，并将剩余的对象段挂到该哈希桶的桶中的自由链表上
		_freeLists[index].pushRange(NextObj(start), end , actualNum - 1);
		return start;
	}

	return nullptr;
}

//申请空间
void* ThreadCache::Allocate(size_t size)
{
	assert(size <= MAX_BYTES); //此时size是从1到256KB之内的，因为ThreadCathe只能给线程存储256KB之内的内存
	size_t aliginSize = SizeClass::RoundUp(size);//此时就算好要申请的空间
	//这里就要计算我们在当前桶哪一个桶
	size_t index = SizeClass::Index(size);

	if (!_freeLists[index].Empty()) //此时表示当前位置不是空的，也就是可以将当前的空间分配出去
	{
		return _freeLists[index].pop();
	}

	else
	{//此时表示自由链表中没有对象，就要找下一层去要，也就是找中心缓存获取对象
		return FetchFromCentralCache(index, aliginSize); //要从中心缓存中获取对齐好之后的内存大小
	}
}

//释放对象时，链表过长时，回收内存到中心缓存
void* ThreadCache::ListTooLong(FreeList& list, size_t size)
{
	void* start = nullptr;
	void* end = nullptr;
	list.popRange(start, end, list.MaxSize()); //此时将一次申请的空间还给Central Cache

	CentralCache::GetInstance()->ReleaseListToSpans(start , size);

	return start;
}

void ThreadCache::Deallocate(void* ptr, size_t size)
{
	assert(ptr);
	assert(size <= MAX_BYTES);
	
	//找到这块空间对应的映射的自由链表的桶
	size_t index = SizeClass::Index(size);

	//直接头插进去就可以了
	_freeLists[index].push(ptr);

	//这里就要判断，如果当前桶上挂得空间较多，那么就要看是否可以将这块内存还回给central cache
	//供其他线程申请时使用
	if (_freeLists[index].Size() >= _freeLists[index].MaxSize())
	{ //此时表示当前链表中挂的比我一次申请的还要多
		ListTooLong(_freeLists[index], size);
	}
}
